﻿using TfsCloudDeploy.Deployment.Service.ServiceContracts;
using TfsCloudDeploy.Deployment.Framework;
using TfsCloudDeploy.Deployment.Framework.Interfaces;
using TfsCloudDeploy.Deployment.Service.DataContracts.Interfaces;
using System.Configuration;
using System;
using TfsCloudDeploy.Deployment.Service.DataContracts;
using System.Runtime.Serialization;
using System.IO;

namespace TfsCloudDeploy.Deployment.Service.ServiceImplementation
{
    public class DeploymentServiceV1 : BaseDeploymentService, IDeploymentServiceV1
    {
        #region Logging

        private static readonly ILog _log = new Log(typeof(DeploymentServiceV1));

        #endregion

        private const string FULL_DEPLOYMENT_LOG_NAME = "detailed.log";
        private const string DEPLOYMENT_FOLDER_NAME = "Logs";

        #region Properties

        public string ToolName
        {
            get
            {
                return ConfigurationManager.AppSettings["ToolName"];
            }
            private set { }
        }

        public string TransportToolName
        {
            get
            {
                return ConfigurationManager.AppSettings["TransportToolName"];
            }
            private set { }
        }

        public string TransportCommand
        {
            get
            {
                return ConfigurationManager.AppSettings["TransportCommand"];
            }
            private set { }
        }

        public string TransportValidationCommand
        {
            get
            {
                return ConfigurationManager.AppSettings["TransportValidationCommand"];
            }
            private set { }
        }

        public string BuildShareRootLocalPath
        {
            get
            {
                return ConfigurationManager.AppSettings["BuildShareRootLocalPath"];
            }
            private set { }
        }

        public string BuildShareRootUncPath
        {
            get
            {
                return ConfigurationManager.AppSettings["BuildShareRootUNCPath"];
            }
            private set { }
        }

        public string ScriptCallArguments
        {
            get
            {
                return ConfigurationManager.AppSettings["ScriptCallArguments"];
            }
            private set { }
        }

        #endregion

        public DeploymentResponseV1 DeployBuild(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "DeployEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 DeployBuildNoBackup(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "DeployNoBackupEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 RollbackBuild(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "RollbackEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 TestBuild(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "TestModeEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 BringOnline(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "BringOnlineEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 TakeOffline(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "TakeOfflineEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 BackupOnly(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "BackupEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public TransportResponseV1 TransportBuild(TransportRequestV1 transportRequest)
        {
            ValidateTransportRequest(transportRequest);

            return (TransportResponseV1)ExecuteTransportCommand((TransportRequestV1)transportRequest);
        }

        public DeploymentResponseV1 CheckServiceStatus(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "CheckServiceStatusEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 CheckServiceInfo(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "CheckServiceInfoEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 ValidateBuild(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "ValidateEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, (IDeploymentRequestV1)deploymentRequest);
        }

        public DeploymentResponseV1 RestartApp(DeploymentRequestV1 deploymentRequest)
        {
            ValidateDeploymentRequest(deploymentRequest);

            string commandNameKey = "RestartAppEntryFunction";
            string commandNameValue = ConfigurationManager.AppSettings[commandNameKey];

            return (DeploymentResponseV1)ExecuteDeploymentCommand(commandNameKey, commandNameValue, deploymentRequest);
        }


        protected void ValidateDeploymentRequest(IDeploymentRequestV1 deploymentRequest)
        {
            _log.Info("DeploymentServiceV2::ValidateDeploymentRequest - ENTER");

            try
            {
                // Verify we have received a value for the build path
                if (string.IsNullOrWhiteSpace(deploymentRequest.BuildPath))
                {
                    throw new ArgumentException("Input error: No value has been provided for required argument 'BuildPath'.", "BuildPath");
                }
                _log.InfoFormat("DeploymentServiceV2::ValidateDeploymentRequest - BuildPath: {0}", deploymentRequest.BuildPath);

                ValidateGeneralSettings();
            }
            catch (Exception ex)
            {
                _log.InfoFormat("DeploymentServiceV2::ValidateDeploymentRequest - ERROR: {0}", ex.Message);
                _log.Error("DeploymentServiceV2::ValidateDeploymentRequest", ex);
                throw;
            }
        }

        protected void ValidateTransportRequest(ITransportRequestV1 transportRequest)
        {
            _log.Info("DeploymentServiceV2::ValidateTransportRequest - ENTER");

            try
            {
                // Verify we have received a value for the source deployment path
                if (string.IsNullOrWhiteSpace(transportRequest.SourceDeploymentPath))
                {
                    throw new ArgumentException("Input error: No value has been provided for required argument 'SourceDeploymentPath'.", "SourceDeploymentPath");
                }
                _log.InfoFormat("DeploymentServiceV2::ValidateTransportRequest - BuildPath: {0}", transportRequest.SourceDeploymentPath);

                // Verify we have received a value for the target deployment path
                if (string.IsNullOrWhiteSpace(transportRequest.TargetDeploymentPath))
                {
                    throw new ArgumentException("Input error: No value has been provided for required argument 'TargetDeploymentPath'.", "TargetDeploymentPath");
                }
                _log.InfoFormat("DeploymentServiceV2::ValidateTransportRequest - BuildPath: {0}", transportRequest.TargetDeploymentPath);

                if (string.IsNullOrWhiteSpace(this.TransportCommand))
                {
                    throw new Exception("Configuration error: TransportCommand is empty. Verify that the setting is specified in web.config.");
                }
                _log.InfoFormat("DeploymentServiceV2::ValidateTransportRequest - TransportCommand: {0}", this.TransportCommand);

                if (string.IsNullOrWhiteSpace(this.TransportValidationCommand))
                {
                    throw new Exception("Configuration error: TransportValidationCommand is empty. Verify that the setting is specified in web.config.");
                }
                _log.InfoFormat("DeploymentServiceV2::ValidateTransportRequest - TransportValidationCommand: {0}", this.TransportValidationCommand);

                ValidateGeneralSettings();
            }
            catch (Exception ex)
            {
                _log.InfoFormat("DeploymentServiceV2::ValidateTransportRequest - ERROR: {0}", ex.Message);
                _log.Error("DeploymentServiceV2::ValidateTransportRequest", ex);
                throw;
            }
        }

        protected void ValidateGeneralSettings()
        {
            // Read the config settings
            string buildShareRootLocalPath = this.BuildShareRootLocalPath;
            string buildShareRootUncPath = this.BuildShareRootUncPath;

            if (string.IsNullOrWhiteSpace(buildShareRootLocalPath) || string.IsNullOrWhiteSpace(buildShareRootUncPath))
            {
                throw new Exception("Configuration error: One or both settings are not initialized: BuildShareRootLocalPath, BuildShareRootUNCPath. " +
                    "Verify that the settings are specified in web.config.");
            }

            _log.InfoFormat("DeploymentServiceV2::ValidateGeneralSettings - buildShareRootLocalPath: {0}", buildShareRootLocalPath);
            _log.InfoFormat("DeploymentServiceV2::ValidateGeneralSettings - buildShareRootUncPath: {0}", buildShareRootUncPath);

            if (string.IsNullOrWhiteSpace(this.ToolName))
            {
                throw new Exception("Configuration error: ToolName is empty. Verify that the setting is specified in web.config.");
            }

            if (string.IsNullOrWhiteSpace(this.TransportToolName))
            {
                throw new Exception("Configuration error: TransportToolName is empty. Verify that the setting is specified in web.config.");
            }

            if (string.IsNullOrWhiteSpace(this.ScriptCallArguments))
            {
                throw new Exception("Configuration error: ScriptCallArguments is empty. Verify that the setting is specified in web.config.");
            }
        }

        protected IDeploymentResponseV1 ExecuteDeploymentCommand(string commandNameKey, string commandNameValue, IDeploymentRequestV1 deploymentRequest)
        {
            if (string.IsNullOrWhiteSpace(commandNameValue))
            {
                throw new Exception(string.Format("Configuration error: {0} is empty. Verify that the setting is specified in web.config.", commandNameKey));
            }

            string transformedPath = TransformBuildPathToLocalPath(deploymentRequest.BuildPath, this.BuildShareRootUncPath, this.BuildShareRootLocalPath);
            string arguments = string.Format("{0} {1}", this.ScriptCallArguments, commandNameValue);

            string executionResults = ExecuteCommand(ToolName, transformedPath, arguments);

            return new DeploymentResponseV1() { BuildOutput = executionResults, IsSuccessful = true };
        }

        protected ITransportResponseV1 ExecuteTransportCommand(ITransportRequestV1 transportRequest)
        {
            string transformedPath = TransformBuildPathToLocalPath(transportRequest.SourceDeploymentPath, this.BuildShareRootUncPath, this.BuildShareRootLocalPath);
            DirectoryInfo sourceFolder = new DirectoryInfo(transportRequest.SourceDeploymentPath);
            string arguments = string.Format(this.TransportCommand, transportRequest.SourceDeploymentPath, Path.Combine(transportRequest.TargetDeploymentPath, sourceFolder.Name));

            string executionResults = ExecuteCommand(TransportToolName, transformedPath, arguments);

            return new TransportResponseV1() { BuildOutput = executionResults, IsSuccessful = true };
        }
    }
}
